﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Linq;

namespace Silverlight.Weblog.Client.DAL.Behaviours
{
    public static class NavigationKeys
    {

        private static List<IHandleNavigationKey> _navigationKeys = new List<IHandleNavigationKey>();

        static NavigationKeys()
        {
            _navigationKeys.Add(new HandleNavigationKey(Key.Down, (verticalOffset, extentHeight, viewportHeight) => verticalOffset + 200));
            _navigationKeys.Add(new HandleNavigationKey(Key.Up, (verticalOffset, extentHeight, viewportHeight) => verticalOffset - 200));
            _navigationKeys.Add(new HandleNavigationKey(Key.End, (verticalOffset, extentHeight, viewportHeight) => extentHeight));
            _navigationKeys.Add(new HandleNavigationKey(Key.Home, (verticalOffset, extentHeight, viewportHeight) => 0));
            _navigationKeys.Add(new HandleNavigationKey(Key.PageDown, true, (verticalOffset, extentHeight, viewportHeight) => verticalOffset + viewportHeight - 150));
            _navigationKeys.Add(new HandleNavigationKey(Key.PageUp, true, (verticalOffset, extentHeight, viewportHeight) => verticalOffset - viewportHeight + 150));
            _navigationKeys.Add(new HandleNavigationKey(Key.Space, (verticalOffset, extentHeight, viewportHeight) => verticalOffset + 150));
        }

        #region HandleNavigationKeyPresses (Attached DependencyProperty)

        public static readonly DependencyProperty HandleNavigationKeyPressesProperty =
            DependencyProperty.RegisterAttached("HandleNavigationKeyPresses", 
            typeof(bool), 
            typeof(NavigationKeys), 
            new PropertyMetadata(false, new PropertyChangedCallback(OnHandleNavigationKeyPressesChanged)));

        public static void SetHandleNavigationKeyPresses(TextBox o, bool value)
        {
            o.SetValue(HandleNavigationKeyPressesProperty, value);
        }

        public static bool GetHandleNavigationKeyPresses(TextBox o)
        {
            return (bool)o.GetValue(HandleNavigationKeyPressesProperty);
        }

        private static void OnHandleNavigationKeyPressesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TextBox sender = (TextBox) d;
            bool NewValue = (bool) e.NewValue;

            if (NewValue)
            {
                sender.KeyUp += new KeyEventHandler(sender_KeyUp);
            }
            else
            {
                sender.KeyUp -= new KeyEventHandler(sender_KeyUp);
                
            }
        }

        static Key[] _keysToHandle = new Key[] { Key.PageUp, Key.PageDown, Key.Space, Key.Home, Key.End, Key.Up, Key.Down};
        static void sender_KeyUp(object sender, KeyEventArgs e)
        {
            if (_keysToHandle.Contains(e.Key))
            {
                e.Handled = true;
            }
        }

        #endregion


        #region ScrollOnNavigationKey (Attached DependencyProperty)

        public static readonly DependencyProperty ScrollOnNavigationKeyProperty =
            DependencyProperty.RegisterAttached("ScrollOnNavigationKey", typeof(bool), typeof(NavigationKeys), new PropertyMetadata(new PropertyChangedCallback(OnScrollOnNavigationKeyChanged)));

        public static void SetScrollOnNavigationKey(ScrollViewer o, bool value)
        {
            o.SetValue(ScrollOnNavigationKeyProperty, value);
        }

        public static bool GetScrollOnNavigationKey(ScrollViewer o)
        {
            return (bool)o.GetValue(ScrollOnNavigationKeyProperty);
        }

        private static void OnScrollOnNavigationKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollViewer sender = (ScrollViewer) d;
            bool value = (bool) e.NewValue;

            if (value)
            {
                sender.KeyUp +=new KeyEventHandler(scrollViewer_KeyUp);
            }
            else
            {
                sender.KeyUp -= new KeyEventHandler(scrollViewer_KeyUp);
            }
        }

        private static void scrollViewer_KeyUp(object sender, KeyEventArgs e)
        {
            ScrollViewer scrollViewer = (ScrollViewer) sender;

            foreach (IHandleNavigationKey navigationKey in _navigationKeys)
            {
                if (e.Key == navigationKey.Key)
                {
                    if (!navigationKey.IsScrollViewerMustNotBeFocused 
                        || (navigationKey.IsScrollViewerMustNotBeFocused && FocusManager.GetFocusedElement() != scrollViewer))
                    {
                        scrollViewer.ScrollToVerticalOffset(navigationKey.GetNewScrollingOffset(scrollViewer.VerticalOffset, scrollViewer.ExtentHeight, scrollViewer.ViewportHeight));
                        e.Handled = true;
                        break;
                    }
                }
            }
        }

        #endregion

        #region EnableMouseWheelScrolling (Attached DependencyProperty)

        public static readonly DependencyProperty EnableMouseWheelScrollingProperty =
            DependencyProperty.RegisterAttached("EnableMouseWheelScrolling", 
            typeof(bool), 
            typeof(NavigationKeys), 
            new PropertyMetadata(new PropertyChangedCallback(OnEnableMouseWheelScrollingChanged)));

        public static void SetEnableMouseWheelScrolling(ScrollViewer o, bool value)
        {
            o.SetValue(EnableMouseWheelScrollingProperty, value);
        }

        public static bool GetEnableMouseWheelScrolling(ScrollViewer o)
        {
            return (bool)o.GetValue(EnableMouseWheelScrollingProperty);
        }

        private static void OnEnableMouseWheelScrollingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollViewer sender = (ScrollViewer)d;
            bool value = (bool)e.NewValue;

            if (value)
            {
                sender.MouseWheel += new MouseWheelEventHandler(sender_MouseWheel);
                // Hack to add Navigation Key support
                sender.Loaded += (s, args) => sender.Focus();
            }
            else
            {
                sender.MouseWheel -= new MouseWheelEventHandler(sender_MouseWheel);
            }
        }

        /// <summary>
        /// Silverlight 3 scrollviewer mouse wheel hack.
        /// When upgrading to silverlight 4, remove this hack. 
        /// Though, we'd probably not get the event in Silverlight 4.
        /// So this can stay here forever, until Silverlight 10 at least.
        /// </summary>
        static void sender_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            ScrollViewer scrollViewer = (ScrollViewer)sender;
            if (Keyboard.Modifiers != ModifierKeys.Control)
            {
                scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - 3 * e.Delta);
            }
        }

        #endregion

    }

    public interface IHandleNavigationKey
    {
        Key Key { get; }
        bool IsScrollViewerMustNotBeFocused { get; }
        double GetNewScrollingOffset(double verticalOffset, double extentHeight, double viewportHeight);
    }

    public class HandleNavigationKey : IHandleNavigationKey
    {
        public HandleNavigationKey(Key key, Func<double, double, double, double> newScrollingOffsetAction)
            : this(key, false, newScrollingOffsetAction)
        {
        }

        public HandleNavigationKey(Key key, bool isScrollViewerMustNotBeFocused, Func<double, double, double, double> newScrollingOffsetAction)
        {
            NewScrollingOffsetAction = newScrollingOffsetAction;
            Key = key;
            IsScrollViewerMustNotBeFocused = isScrollViewerMustNotBeFocused;
        }

        public Func<double, double, double, double> NewScrollingOffsetAction { get; private set; }
        public Key Key { get; private set; }
        public bool IsScrollViewerMustNotBeFocused { get; private set; }
        public double GetNewScrollingOffset(double verticalOffset, double extentHeight, double viewportHeight)
        {
            return NewScrollingOffsetAction(verticalOffset, extentHeight, viewportHeight);
        }
   }
}
